package com.demo.spring.security.config.oauth2;

import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.Arrays;

@Configuration
@EnableAuthorizationServer
@RequiredArgsConstructor(onConstructor=@__({@Autowired,@Lazy}))
public class CusAuthServerJwtConfig extends AuthorizationServerConfigurerAdapter {
    /** 数据源 */
    private final DataSource dataSource;
    /** 令牌存储 */
    private final TokenStore tokenStore;
    /** 认证管理器 */
    private final AuthenticationManager authenticationManager;
    /** JWT信息转换器 */
    private final JwtAccessTokenConverter jwtAccessTokenConverter;
    /** JWT自定义信息增强器 */
    private final CusAccessTokenEnhancer cusAccessTokenEnhancer;
    /** 授权令牌服务 */
    private final AuthorizationServerTokenServices authorizationServerTokenServices;
    /** 授权码服务 */
    private final AuthorizationCodeServices authorizationCodeServices;
    /** 客户端服务，从数据库存取 */
    @Setter(onMethod=@__({@Autowired,@Qualifier("jdbcClientDetailsService")}))
    private ClientDetailsService jdbcClientDetailsService;

    @Bean("jdbcClientDetailsService")
    public ClientDetailsService jdbcClientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    /** 授权令牌服务 */
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setSupportRefreshToken(true);
        // 配置客户端服务
        services.setClientDetailsService(jdbcClientDetailsService);
        // 配置令牌存储
        services.setTokenStore(tokenStore);
        // 配置JWT转换器和JWT自定义信息增强器
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter, cusAccessTokenEnhancer));
        services.setTokenEnhancer(tokenEnhancerChain);
        return services;
    }

    /** 授权码存储配置，配置为内存存储 */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients()
                // 放行AccessToken校验接口（/oauth/check_token？），当资源服务器拿到AccessToken需要校验其合法性时，会调用它
                .checkTokenAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 配置客户端服务，从数据库存取
        clients.withClientDetails(jdbcClientDetailsService);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                // 配置security认证管理器
                .authenticationManager(authenticationManager)
                // 配置授权码服务
                .authorizationCodeServices(authorizationCodeServices)
                // 配置令牌服务
                .tokenServices(authorizationServerTokenServices);
    }

}
